#include "./../inc/animation.h"
#include "./../inc/view.h"
#include "./../inc/timer.h"
#include "system.h"
#include "render.h"

//#include "ustring.h"

#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <math.h>

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>

typedef struct _UFO_Animation_Internal
{
	char *id;
	int duration;
	int delay;
	long begintime;
	UFO_Animation_Value fromValue;
	UFO_Animation_Value toValue;
	//UFO_String _toValue;
	UFO_Animation_Value finalValue;
	
	int autoReverses;
	int repeatTime, realRepeatTime;

	UFO_Animation_TimingFunction timingFunction;
	
	ANIMATIONPROC callback;

	UFO_Timer timer;
	UFO_View view;
	void *userdata;

	UFO_Handle handle;
}UFO_Animation_Internal, *PUFO_Animation_Internal;

float internal_calcInterpolation(UFO_Animation_TimingFunction timingFunc, float s, float e,
	float duration, float t)
{
	if (s == e || duration == 0)
		return s;

	switch (timingFunc) {
	case TimingFunctionLinear:
		{
			return s + (e-s) * t/duration;
		}

	case TimingFunctionEaseIn:
		{
			float dis = e-s;
			float speed = 0.0f;
			float accelerate = 2*(dis-speed*duration)/(duration*duration);
			return s + speed * t + accelerate * t * t/2;
		}

	case TimingFunctionDefault:
	case TimingFunctionEaseOut:
		{ 
			float dis = e-s;
			float speed = 2.0f*dis/duration;
			float accelerate = 2*(dis-speed*duration)/(duration*duration);
			return s + speed * t + accelerate * t * t/2;
		}

	case TimingFunctionEaseInEaseOut:
		{
			if (t <= duration/2)
				return internal_calcInterpolation(TimingFunctionEaseIn, s, e-(e-s)/2, duration/2, t);
			else
				return internal_calcInterpolation(TimingFunctionEaseOut, s+(e-s)/2, e, duration/2, t-duration/2);
		}
            
    case TimingFunctionQuinticEaseOut:
        {
            float c = e-s;
            return c * ((t= t/duration-1) * t * t *t * t + 1)+ s;
        }
    
    case TimingFunctionCubicEaseOut:
        {
            float c = e-s;
            return c*((t=t/duration-1)*t*t + 1) + s;
        }
            
    case TimingFunctionQuadraticEaseOut:
        {
            float c = e-s;
            return -c * (t /= duration) * (t - 2) + s;
        }
            
    case TimingFunctionExponentialEaseOut:
        {
            float c= e-s;
            return (t==duration) ? s+c : c * (-pow(2, -10 * t/duration) + 1) + s;
        }
	}
	return 0;
}

UFO_Animation ufoAnimationCreate(UFO_View view)
{
	PUFO_Animation_Internal pAni = malloc(sizeof(UFO_Animation_Internal));
	memset(pAni, 0, sizeof(UFO_Animation_Internal));

	pAni->view = view;
	pAni->handle = (UFO_Handle)ufoCreateHandle(UFO_ID_ANIMATION, pAni);

	return (UFO_Animation)pAni->handle;
}

int default_TimerProc(UFO_Timer timer, int deltaTime, void *userdata)
{
	PUFO_Animation_Internal pAni = userdata;
	if (pAni) {

		long curTime = ufoGetCurTime();
		long newTime = curTime - (pAni->begintime + pAni->delay);

		if (curTime < pAni->begintime + pAni->delay || newTime <= 0) {//### not reach fire time
			return 1;
		}

		if (newTime < pAni->duration && pAni->repeatTime) {

			if (pAni->toValue.type & Animation_rect) {
				ufoRect rectCal = {0};

				rectCal.left = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.rect.left, pAni->toValue.rect.left, (float)pAni->duration, (float)newTime);
				rectCal.top = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.rect.top, pAni->toValue.rect.top, (float)pAni->duration, (float)newTime);
				rectCal.right = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.rect.right, pAni->toValue.rect.right, (float)pAni->duration, (float)newTime);
				rectCal.bottom = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.rect.bottom, pAni->toValue.rect.bottom, (float)pAni->duration, (float)newTime);

				ufoViewSetRect(pAni->view, &rectCal);
			}

			if (pAni->toValue.type & Animation_alpha) {
				float fAlpha = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.alpha, pAni->toValue.alpha, (float)pAni->duration, (float)newTime);
				ufoViewSetAlpha(pAni->view, fAlpha);
			}

			if (pAni->toValue.type & Animation_colorMask) {
				float redMask = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.redMask, pAni->toValue.redMask, (float)pAni->duration, (float)newTime);
				float greenMask = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.greenMask, pAni->toValue.greenMask, (float)pAni->duration, (float)newTime);
				float blueMask = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.blueMask, pAni->toValue.blueMask, (float)pAni->duration, (float)newTime);
				//ufoViewSetColorMask(pAni->view, redMask, greenMask, blueMask);
			}

			if (pAni->toValue.type & Animation_angle) {
				float fAngle = internal_calcInterpolation(pAni->timingFunction, 
					pAni->fromValue.angle, pAni->toValue.angle, (float)pAni->duration, (float)newTime);
				ufoViewSetAngle(pAni->view, fAngle);
			}
            
			if (pAni->toValue.type & Animation_axisZ) {
				float fAxisZ = (float)internal_calcInterpolation(pAni->timingFunction,
                                                                 pAni->fromValue.z, pAni->toValue.z, 
																 (float)pAni->duration, (float)newTime);
				ufoViewSetZ(pAni->view, fAxisZ);
			}
		}
		else {//### over time

			if (pAni->toValue.type & Animation_rect) {//### set final state
				ufoViewSetRect(pAni->view, &pAni->toValue.rect);
			}
			
			if (pAni->toValue.type & Animation_alpha) {
				ufoViewSetAlpha(pAni->view, pAni->toValue.alpha);
			}

			if (pAni->toValue.type & Animation_colorMask) {
				//ufoViewSetColorMask(pAni->view, pAni->toValue.redMask, pAni->toValue.greenMask, pAni->toValue.blueMask);
			}

			if (pAni->toValue.type & Animation_angle) {
				ufoViewSetAngle(pAni->view, pAni->toValue.angle);
			}
            
            if (pAni->toValue.type & Animation_axisZ) {
                ufoViewSetZ(pAni->view, pAni->toValue.z); //### no here
            }
			
			if (pAni->autoReverses) {
				UFO_Animation_Value tempSwap = pAni->fromValue;
				pAni->fromValue = pAni->toValue;
				pAni->toValue = tempSwap;
			}

			--pAni->repeatTime;

			pAni->begintime = curTime - pAni->delay;

			if (pAni->repeatTime == 0) {
				if (pAni->toValue.type & Animation_rect)
					ufoViewSetRect(pAni->view, &pAni->finalValue.rect);//### set final state
				
				if (pAni->toValue.type & Animation_alpha)
					ufoViewSetAlpha(pAni->view, pAni->finalValue.alpha);

				if (pAni->toValue.type & Animation_colorMask) {
					//ufoViewSetColorMask(pAni->view, pAni->finalValue.redMask, pAni->finalValue.greenMask, pAni->finalValue.blueMask);
				}
				if (pAni->toValue.type & Animation_angle)
					ufoViewSetAngle(pAni->view, pAni->finalValue.angle);

                if (pAni->toValue.type & Animation_axisZ)
                    ufoViewSetZ(pAni->view, pAni->finalValue.z);
                
				ufoAnimationStop((UFO_Animation)pAni->handle);//### animation stop here
				if (pAni->callback)//### notify callback
					pAni->callback(pAni->handle, UFO_ANIMATION_FINISH, pAni->userdata);
				
				ufoViewSendMessage(pAni->view, MSG_ANIMATION_FINISH, pAni->handle, 0);//### notify view
			}
		}

	}

	return 0;
}

int ufoAnimationDelete(UFO_Animation animation)
{

	return 0;
}

int ufoAnimationPlay(UFO_Animation animation)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		ufoAnimationStop(animation);

		pAni->repeatTime = pAni->realRepeatTime;//### reset repeatTime
		
		pAni->fromValue.type = pAni->toValue.type;
		pAni->fromValue.alpha = ufoViewGetAlpha(pAni->view);
		ufoViewGetRect(pAni->view, &pAni->fromValue.rect);
		pAni->fromValue.angle = ufoViewGetAngle(pAni->view);
		pAni->fromValue.z = ufoViewGetZ(pAni->view);
		//ufoViewGetColorMask(pAni->view, &pAni->fromValue.redMask, &pAni->fromValue.greenMask, &pAni->fromValue.blueMask);

		if (pAni->repeatTime <= 0) {
			pAni->repeatTime = 1;
		}

		if (pAni->duration == 0) {
			pAni->duration = 250;
		}

		if (pAni->autoReverses) {
			pAni->repeatTime *= 2;
			pAni->duration /= 2;
			pAni->finalValue = pAni->fromValue;
		}
		else {
			pAni->finalValue = pAni->toValue;
		}
        
        if (!(pAni->toValue.type&Animation_axisZ) && !pAni->view) {
            return 0; 
        }

		pAni->timer = ufoTimerCreate(0, 0, -1, default_TimerProc, pAni);
		pAni->begintime = ufoGetCurTime();

		return 1;
	}

	return 0;
}

int ufoAnimationStop(UFO_Animation animation)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni && pAni->timer) {
		ufoTimerDelete(pAni->timer);
		pAni->timer = 0;
	}
	return 0;
}

int ufoAnimationSetDuration(UFO_Animation animation, int duration)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		pAni->duration = duration;
		return 1;
	}

	return 0;
}

int ufoAnimationSetDelay(UFO_Animation animation, int delay)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		pAni->delay = delay;
		return 1;
	}

	return 0;
}

int ufoAnimationSetToValue(UFO_Animation animation, UFO_Animation_Value value)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		pAni->toValue = value;
		return 1;
	}
	return 0;
}

int ufoAnimationSetAutoreverses(UFO_Animation animation, int reverses)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		pAni->autoReverses = reverses;
		return 1;
	}

	return 0;
}

int ufoAnimationSetRepeatTime(UFO_Animation animation, int repeatTime)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		pAni->realRepeatTime = repeatTime;
		return 1;
	}
	return 0;
}

int ufoAnimationSetTimingFunction(UFO_Animation animation, UFO_Animation_TimingFunction timingFunction)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {

		pAni->timingFunction = timingFunction;

		return 1;
	}

	return 0;
}

int ufoAnimationSetCallback(UFO_Animation animation, ANIMATIONPROC proc, void* userdata)
{
	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni) {
		pAni->userdata = userdata;
		pAni->callback = proc;
		return 1;
	}

	return 0;
}

int ufoAnimationGetState(UFO_Animation animation)
{
 	PUFO_Animation_Internal pAni = ufoObjectFromHandle((UFO_Handle)animation);
	if (pAni && pAni->timer) {
        
		return UFO_ANIMATION_PLAYING;
	}
    
	return 0;
}

